﻿using DotNetCommon;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DBUtil.Builders;

/// <summary>
/// 用于 builder.SetColumn/IgnoreColumns/OnlyColumns()
/// </summary>
internal class SetIgnoreItem
{
    #region ctor prop
    public SetIgnoreItem(EnumSetIgnoreType type, string[] columnPureNames, object val, List<string> jsonDepthProps)
    {
        AssertUtil.NotNullOrEmpty(columnPureNames);
        ColumnNamesPure = columnPureNames;
        Type = type;
        Value = val;
        JsonDepthProps = jsonDepthProps;
    }
    public EnumSetIgnoreType Type { get; set; }
    /// <summary>
    /// 是有当 OnlyColumns 或 IgnoreColumns 才会 Count>1;<br/>
    /// 对于 SetColumn(i=>new {i.CreateTime,i.UpdateTime},DateTime.Now) 会生成两个 SetIgnoreItem
    /// </summary>
    public string[] ColumnNamesPure { get; set; }
    public object Value { get; set; }
    /// <summary>
    /// 对于设置的 JsonMap 深层次有用, 如: 
    /// <code>
    /// .SetColumn(i=>i.Ext.CreateTime, DateTime)// 有: JsonDepthProps=["Ext","CreateTime"]
    /// </code>
    /// </summary>
    public List<string> JsonDepthProps { get; set; }
    #endregion

    #region static methods
    /// <summary>
    /// 向已有的 setIgnores 添加 SetColumn
    /// </summary>
    internal static void SetColumn(List<SetIgnoreItem> setIgnores, string columnPureName, object value)
    {
        if (value is RawString raw) value = BuilderHelper.TrimRawStringForSeg(raw);
        setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [columnPureName], value, null));
    }
    /// <summary>
    /// 向已有的 setIgnores 添加 IgnoreColumn
    /// </summary>
    internal static void IgnoreColumns(List<SetIgnoreItem> setIgnores, params string[] columnPureNames)
    {
        setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.IgnoreColumn, columnPureNames, null, null));
    }
    /// <summary>
    /// 向已有的 setIgnores 添加 OnlyColumn
    /// </summary>
    internal static void OnlyColumns(List<SetIgnoreItem> setIgnores, params string[] columnPureNames)
    {
        setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.OnlyColumn, columnPureNames, null, null));
    }

    /// <summary>
    /// 根据属性选择表达式, 向已有的 setIgnores 添加 SetColumn, 示例:
    /// <code>
    /// SetIgnoreItem.SetColumn(setIgnores, entityInfo, i=> new { i.Ext.CreateTime, i.UpdateTime }, DateTime.Now)
    /// </code>
    /// </summary>
    internal static void SetColumn<T>(List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector, object value)
    {
        var names = GetDepthPropNames(propSelector);
        if (names.IsNullOrEmpty()) throw new Exception($"格式错误,无法获取指定的列: {propSelector}!");
        if (value is RawString raw) value = BuilderHelper.TrimRawStringForSeg(raw);
        SetColumn(setIgnores, entityInfo, names, value);
    }

    /// <summary>
    /// 根据属性名称 向已有的 setIgnores 添加 SetColumn, 示例:
    /// <code>
    /// SetIgnoreItem.SetColumn(setIgnores, entityInfo, [["Ext","CreatTime"],["UpdateTime"]], DateTime.Now)
    /// </code>
    /// </summary>
    internal static void SetColumn(List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, List<List<string>> propPureNames, object value)
    {
        if (value is RawString raw) value = BuilderHelper.TrimRawStringForSeg(raw);
        for (var i = 0; i < propPureNames.Count; i++)
        {
            var multiDepthPropNames = propPureNames[i];
            var prop = entityInfo.Props.FirstOrDefault(j => j.PropNamePure == multiDepthPropNames[0]);
            if (prop == null) continue;
            if (multiDepthPropNames.Count > 1)
            {
                //JsonDepthProps 如: ["Ext","CreatTime"]
                multiDepthPropNames.RemoveAt(0);
                setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [prop.ColumnNamePure], value, multiDepthPropNames));
            }
            else
            {
                //当做普通列
                setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.SetColumn, [prop.ColumnNamePure], value, null));
            }
        }
    }

    /// <summary>
    /// 根据属性选择表达式, 向已有的 setIgnores 添加 IgnoreColumn, 示例:
    /// <code>
    /// SetIgnoreItem.IgnoreColumns(setIgnores, entityInfo, i=> new { i.Ext.CreateTime, i.UpdateTime }, DateTime.Now)
    /// </code>
    /// </summary>
    internal static void IgnoreColumns<T>(List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector)
    {
        var names = GetDepthPropNames(propSelector);
        if (names.Any(i => i.Count > 1)) throw new Exception($"只能忽略最外层属性, 如([JsonMap]public ExtInfo Ext {{ get; set; }} ): update.IgnoreColumns(i=>i.Ext.CreateTime) 是错的");
        var outerNames = names.Select(i => i[0]).ToList();
        var props = entityInfo.Props.Where(i => outerNames.Contains(i.PropNamePure)).ToArray();
        setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.IgnoreColumn, props.Select(i => i.ColumnNamePure).ToArray(), null, null));
    }

    /// <summary>
    /// 根据属性选择表达式, 向已有的 setIgnores 添加 OnlyColumn, 示例:
    /// <code>
    /// SetIgnoreItem.OnlyColumns(setIgnores, entityInfo, i=> new { i.Ext.CreateTime, i.UpdateTime }, DateTime.Now)
    /// </code>
    /// </summary>
    internal static void OnlyColumns<T>(List<SetIgnoreItem> setIgnores, EntityInfo entityInfo, Expression<Func<T, object>> propSelector)
    {
        var names = GetDepthPropNames(propSelector);
        if (names.Any(i => i.Count > 1)) throw new Exception($"只能选择最外层属性, 如([JsonMap]public ExtInfo Ext {{ get; set; }} ): update.OnlyColumns(i=>i.Ext.CreateTime) 是错的");
        var outerNames = names.Select(i => i[0]).ToList();
        var props = entityInfo.Props.Where(i => outerNames.Contains(i.PropNamePure)).ToArray();
        setIgnores.Add(new SetIgnoreItem(EnumSetIgnoreType.OnlyColumn, props.Select(i => i.ColumnNamePure).ToArray(), null, null));
    }

    /// <summary>
    /// 获取深度属性名称列表, 传入:
    /// <code>
    /// i=>new {i.Ext.Detail.Name,i.Ext.Detail.Id}
    /// </code>
    /// 返回:
    /// <code>
    /// [["Ext","Detail","Name"],["Ext","Detail","Id"]]
    /// </code>
    /// </summary>
    internal static List<List<string>> GetDepthPropNames(LambdaExpression expression)
    {
        if (expression.Body.NodeType == ExpressionType.Convert)
        {
            return Func((expression.Body as UnaryExpression).Operand);
        }
        return Func(expression.Body);

        List<List<string>> Func(Expression expression)
        {
            if (expression.NodeType == ExpressionType.MemberInit)
            {
                //new CustomClass{...}
                var names = new List<List<string>>();
                var initExp = expression as MemberInitExpression;
                foreach (var bind in initExp.Bindings)
                {
                    if (bind is not MemberAssignment assign || assign?.Expression is not MemberExpression member) throw new Exception($"格式错误({bind}): 使用 new CustomClass{{...}} 构建的属性必须是 MemberAssignment/MemberExpression, 如: SetColumn(i=>new Person{{i.CreateTime,i.UpdateTime}})!");
                    var _names = new List<string>(4);
                    Visit(member, _names);
                    _names.Reverse();
                    names.Add(_names);
                }
                return names;
            }
            else if (expression.NodeType == ExpressionType.New)
            {
                //new {...}
                var names = new List<List<string>>();
                var newExp = expression as NewExpression;
                if (!newExp.Type.IsAnonymous()) throw new Exception($"格式错误({expression}): 使用 new() 构建的必须是匿名类,如: SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
                foreach (var arg in newExp.Arguments)
                {
                    if (arg is not MemberExpression member) throw new Exception($"格式错误({arg}): 使用 new() 构建的匿名类属性必须是 MemberExpression, 如: SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
                    var _names = new List<string>(4);
                    Visit(arg, _names);
                    _names.Reverse();
                    names.Add(_names);
                }
                return names;
            }
            else if (expression.NodeType == ExpressionType.Parameter)
            {
                // p=>p
                throw new Exception($"格式错误, 不允许使用【i=>i】作为 SetColumn() 的参数, 正确的示例: SetColumn(i=>i.Name) 或 SetColumn(i=>new{{i.CreateTime,i.UpdateTime}})!");
            }
            else if (expression.NodeType == ExpressionType.MemberAccess)
            {
                //p=>p.Id
                var _names = new List<string>(4);
                Visit(expression, _names);
                _names.Reverse();
                return [_names];
            }
            else if (expression is MethodCallExpression call)
            {
                if (call.Type.FullName == "System.Text.Json.Nodes.JsonNode" && call.Method.GetMethodFullName() == "System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)")
                {
                    //p=>p.Ext["Detail"]
                    var _names = new List<string>(4);
                    Visit(call, _names);
                    _names.Reverse();
                    return [_names];
                }
                else if (call.Object?.Type.IsSubOrAssignableTo<System.Collections.IDictionary>() == true)
                {
                    //p=>p.Ext["Detail"]
                    var _names = new List<string>(4);
                    Visit(call, _names);
                    _names.Reverse();
                    return [_names];
                }
            }

            throw new Exception($"格式错误: {expression}");
        }

        void Visit(Expression expression, List<string> names)
        {
            if (expression is MemberExpression memberExpression)
            {
                //p=>p.Ext.Detail.Name
                //过滤掉 SetColumn(i=>i.Age.Value,10) 中的 Value
                if (memberExpression.Member.Name != "Value" || !memberExpression.Expression.Type.IsNullable())
                {
                    names.Add(memberExpression.Member.Name);
                }
                if (memberExpression.Expression is MemberExpression || memberExpression.Expression is MethodCallExpression)
                    Visit(memberExpression.Expression, names);
            }
            else if (expression is MethodCallExpression callExpression)
            {
                //p=>p.Ext["Detail"]["Name"]
                if (callExpression.Arguments[0].Type.IsNumeric())
                {
                    throw new Exception($"格式错误, SetColumn(exp,...) 中的exp不能有数字索引, 错误的示范: SetColumn(i=>i.Logs[1].Message,...): {expression}");
                }
                names.Add((callExpression.Arguments[0] as ConstantExpression).Value.ToString());
                if (callExpression.Object is MethodCallExpression callExpression2 && callExpression2.Type.FullName == "System.Text.Json.Nodes.JsonNode" && callExpression2.Method.GetMethodFullName() == "System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)")
                {
                    Visit(callExpression2, names);
                }
                else if (callExpression.Object is MethodCallExpression callExpression3 && callExpression3.Object?.Type.IsSubOrAssignableTo<System.Collections.IDictionary>() == true)
                {
                    Visit(callExpression3, names);
                }
                else if (callExpression.Object is MemberExpression memberExpression2)
                {
                    Visit(memberExpression2, names);
                }
            }
        }
    }
    #endregion
}
